Skip to content

feat(lua): add lvgl.bitmap object - #7971

Closed
thomaskistler wants to merge 2 commits into
EdgeTX:mainfrom
thomaskistler:thomaskistler/lvgl-bitmap-widget
Closed

thomaskistler wants to merge 2 commits into
EdgeTX:mainfrom
thomaskistler:thomaskistler/lvgl-bitmap-widget

Conversation

@thomaskistler

Copy link
Copy Markdown

Fixes: N/A (feature; no linked issue). Alternative to #7947, which adds a bitmap source to lvgl.image.

Summary of changes:

Adds lvgl.bitmap([parent, ]params), a separate Lua LVGL object (also lvgl.BITMAP for lvgl.build) that wraps StaticBitmap. lvgl.image is not modified.

local tile = lvgl.bitmap({ file = "/IMAGES/tile.jpg", x = 0, y = 0, w = 256, h = 256 })
tile:set({ file = "/IMAGES/other.jpg" })

Motivation

lvgl.image decodes through LVGL's shared image cache, which has 8 entries (LV_IMG_CACHE_DEF_SIZE). A Lua widget that shows more images than that, for example a map made of 256x256 JPEG tiles, keeps evicting images that are still visible and decodes them again on redraw. lvgl.bitmap decodes once when its file changes and the object owns the pixels, so a widget can keep one object per image and decide for itself which ones stay decoded.

Details

  • file is a string or a function, like lvgl.image. The file is only decoded when it changes. A function is only called while the object is visible. Setting a string replaces a previous function.
  • The image is scaled to fit the frame when it is decoded and centered. Changing w/h reloads it. Hidden objects keep their pixels.
  • StaticBitmap::setSource(filename) now chooses the format from the file like BitmapBuffer::loadBitmap: RGB565 for opaque images, ARGB4444 for images with alpha. setSource(filename, fmt) forces a format, and the constructor takes the same optional format. Existing callers (model select, file preview, model bitmap widget) pass BMP_ARGB4444 explicitly, so their rendering does not change.
  • Previously StaticBitmap always used ARGB4444, which keeps 4 bits per channel and stores 3 bytes per pixel. For an opaque JPEG tile, an ad-hoc host measurement gave 192 KB and 31.1 dB against the 8-bit decode, versus 128 KB and 39.9 dB for RGB565. With the format chosen from the file, opaque images are byte-identical to what Bitmap.open produces.
  • The pixel memory is not counted against the Lua bitmap memory limit (LUA_MEM_EXTRA_MAX).
  • A file that cannot be loaded leaves the object blank.
  • ETX_BITMAP is inserted after ETX_QRCODE, so the numeric values of later lvgl.* type constants shift by one.
  • Documentation is in the lvgl.bitmap luadoc block. It is marked as introduced in 3.0.0.

Validation

Built and tested the native TX16S target on macOS:

  • cmake -DCMAKE_BUILD_TYPE=Debug -DPCB=X10 -DPCBREV=TX16S .. in build/, then cmake --build . --target native-configure and cmake --build native --target gtests-radio
  • native/gtests-radio: 259 tests passed, including 17 new LuaLvglBitmap tests

Not done: ARM firmware and Companion builds, and testing on a radio.

thomaskistler and others added 2 commits November 1, 2026 09:05
StaticBitmap always loaded images as ARGB4444, which reduces every channel
to 4 bits and then expands the result to RGB565 plus an 8-bit alpha channel
(3 bytes per pixel). For opaque images such as JPEG that loses colour
precision and costs 50% more memory than the 2 bytes per pixel of RGB565.

Mirror BitmapBuffer::loadBitmap: setSource(filename) now chooses the format
from the file (RGB565 for opaque images, ARGB4444 for images with alpha),
and setSource(filename, fmt) forces one. The constructor takes the same
optional format. RGB565 images are scaled to fit the frame like the ARGB4444
path does, and displayed without an alpha channel.

Existing callers (model select, file preview, model bitmap widget) pass
BMP_ARGB4444 explicitly, so their rendering does not change.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Add lvgl.bitmap([parent, ]params), a separate object from lvgl.image whose
decoded pixels are owned by the object instead of the shared LVGL image
cache, which holds only 8 entries. A widget that needs a set of images to
stay decoded, such as map tiles, can keep one object per image and only
decode when the file changes.

- `file` accepts a string or a function, like lvgl.image
- the image is decoded and scaled to fit the frame when the source changes;
  resizing the frame reloads it
- hidden objects keep their pixels
- opaque images keep full RGB565 colour (2 bytes per pixel); images with
  alpha use ARGB4444 (3 bytes per pixel)
- lvgl.image is not modified

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant